package com.ruoyi.business.service.impl;

import com.alibaba.fastjson.JSON;
import com.ruoyi.business.domain.CarPackageAudit;
import com.ruoyi.business.domain.ServiceItem;
import com.ruoyi.business.service.ICarPackageAuditService;
import com.ruoyi.business.service.IProcessService;
import com.ruoyi.business.vo.AuditInfo;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.system.service.ISysUserService;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * Description：
 * 流程activiti相关API调用，实现流程部署、删除等等
 *
 * @author Gekor
 * @date Create on 2021/7/21 15:08
 */
@Service
public class ProcessServiceImpl implements IProcessService {

	@Autowired
	private RepositoryService repositoryService;
	@Autowired
	private RuntimeService runtimeService;
	@Autowired
	private TaskService taskService;
	@Autowired
	private HistoryService historyService;
	@Autowired
	private ISysUserService sysUserService;
	@Autowired
	private ICarPackageAuditService carPackageAuditService;

	@Override
	public Task selectExcutingTaskByProcessInstanceId(String processInstanceId) {
		return taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
	}

	/**
	 * 根据用户id查询他已完成的任务集合
	 *
	 * @param userId
	 * @return
	 */
	@Override
	public List<HistoricTaskInstance> selectDoneTaskList(Long userId) {
		return historyService.createHistoricTaskInstanceQuery()
				.taskAssignee(String.valueOf(userId))
				.list();
	}

	@Override
	public void completeTaskByApplicant(String taskId) {
		taskService.complete(taskId);
	}

	@Override
	public void addAssignee(String taskId, String createBy) {
		taskService.setAssignee(taskId, createBy);
	}

	/**
	 * 根据任务ID查询正在执行的流程实例
	 *
	 * @param taskId
	 * @return
	 */
	@Override
	public ProcessInstance selectProcessInstanceByTaskId(String taskId) {
		return runtimeService.createProcessInstanceQuery()
				.processInstanceId(
						taskService.createTaskQuery()
								.taskId(taskId)
								.singleResult()
								.getProcessInstanceId()
				).singleResult();
	}

	/**
	 * 认领、审批：给流程变量设置布尔值，设置批注信息,完成任务，
	 *
	 * @param taskId
	 * @param auditStatus
	 * @param comment
	 * @param instanceId
	 * @return
	 */
	@Override
	public Task completeTaskByAudit(String taskId, boolean auditStatus, String comment, String instanceId) {
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		if (comment != null) { // 没有评论表示是审核人节点，不是重新申请节点,需要跟进definitionKey变量来控制是否同意
			taskService.claim(taskId, String.valueOf(ShiroUtils.getUserId()));
			String taskDefinitionKey = task.getTaskDefinitionKey();
			taskService.setVariable(taskId, taskDefinitionKey, auditStatus);
			String msg = "[" + (auditStatus ? "同意" : "拒绝") + "] : " + comment;
			taskService.addComment(taskId, instanceId, msg);
		} else {// 申请人重新申请节点,更新money流程变量的值,我们一般都使用全局的流程变量，所有通过那个taskId设置都是可以的，只要在使用之前设置
			CarPackageAudit audit = carPackageAuditService.selectCarPackageAuditByInstanceId(instanceId);
			ServiceItem serviceItem = JSON.parseObject(audit.getServiceItemInfo(), ServiceItem.class);
			taskService.setVariable(taskId,"money",serviceItem.getDiscountPrice().longValue());
		}
		taskService.complete(taskId);
		ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
		if (processInstance == null) {
			return null;
		}
		// 都是排它网关，直接 singleResult
		return taskService.createTaskQuery().processInstanceId(instanceId).singleResult();
	}

	/**
	 * 根据当前登录用户查询他待办任务所在的流程实例id
	 *
	 * @return
	 */
	@Override
	public List<String> selectProcessInstanceWithLoginUser() {
		String userId = ShiroUtils.getUserId().toString();
		List<Task> tasks = taskService.createTaskQuery().taskCandidateOrAssigned(userId).active().list();
		List<String> instanceIds = new ArrayList<>();
		for (Task task : tasks) {
			instanceIds.add(task.getProcessInstanceId());
		}
		return instanceIds;
	}

	@Override
	public void deleteProcessInstance(String instanceId) {
		runtimeService.deleteProcessInstance(instanceId, "流程撤销了");
	}

	@Override
	public String selectProcessDefinitonIdByInstanceId(String instanceId) {
		HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();
		return processInstance.getProcessDefinitionId();
	}

	@Override
	public String selectTaskDefinitionKeyByTaskId(String taskId) {
		return taskService.createTaskQuery().taskId(taskId).singleResult().getTaskDefinitionKey();
	}

	@Override
	public List<AuditInfo> selectHistoricAudit(String instanceId) {
		List<HistoricActivityInstance> activityList = historyService.createHistoricActivityInstanceQuery()
				.processInstanceId(instanceId)
				.activityType("userTask")
				.finished()
				.list();
		List<AuditInfo> auditInfoList = new ArrayList<>();
		if (activityList == null || activityList.size() == 0)
			return Collections.EMPTY_LIST;
		for (HistoricActivityInstance activity : activityList) {
			if (activity.getAssignee() == null) {
				continue;    // 没有处理人，说明此节点虽然完成了，但是是因为撤销了，所有没有处理人，审批信息没有也不用展示
			}
			AuditInfo info = new AuditInfo();
			BeanUtils.copyProperties(activity, info);    // 拷贝相同属性

			SysUser sysUser = sysUserService.selectUserById(Long.valueOf(activity.getAssignee()));
			info.setAuditor(sysUser.getUserName());
			String comment = getComment(activity.getTaskId());
			info.setComment(comment);
			auditInfoList.add(info);
		}

		return auditInfoList;
	}

	@Override
	public String getComment(String taskId) {
		List<Comment> comments = taskService.getTaskComments(taskId, "comment");
		if (comments != null && comments.size() > 0) {
			return comments.get(0).getFullMessage();
		} else {
			return "";
		}
	}

	@Override
	public void addCandidateUser(String taskId, Long userId) {
		taskService.addCandidateUser(taskId, String.valueOf(userId));
	}

	@Override
	public Task getExecutingTask(String processInstanceId) {
		return taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
	}

	@Override
	public ProcessInstance startProcessInstance(String definitionKey, String businessKey, Map<String, Object> variables) {
		return runtimeService.startProcessInstanceByKey(definitionKey, businessKey, variables);
	}

	@Override
	public InputStream getImgInputStream(String definitionId, List<String> highLightedActivities, List<String> highLightedFlows) {
		BpmnModel model = repositoryService.getBpmnModel(definitionId);
		ProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
		//generateDiagram(流程模型,需要高亮的节点,需要高亮的线条,后面三个参数都表示是字体)
		return generator.generateDiagram(model, highLightedActivities, highLightedFlows, "宋体", "宋体", "宋体");
	}

	@Override
	public ProcessDefinition selectProcessDefinitonByDeployId(String deploymentId) {
		return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
	}

	@Override
	public Deployment deploy(String filepath) throws FileNotFoundException {
		Deployment deployment = null;

		if (filepath.endsWith("zip")) {
			InputStream fileInputStream = new FileInputStream(filepath);
			deployment = repositoryService.createDeployment()
					.addZipInputStream(new ZipInputStream(fileInputStream)).deploy();
		} else if (filepath.endsWith("bpmn")) {
			deployment = repositoryService.createDeployment()
					.addInputStream(filepath, new FileInputStream(filepath)).deploy();
		}
		return deployment;
	}
}
